﻿Imports System.Data.Common
Imports System.ComponentModel
Imports System.Reflection
Imports System.Threading

<DefaultMember("Item")> _
Friend Class AdoDotNetConnectionProperties
    Implements IDataConnectionProperties, ICustomTypeDescriptor
    ' Events
    Public Event PropertyChanged(ByVal sender As Object, ByVal e As EventArgs) Implements IDataConnectionProperties.PropertyChanged

    'Public Custom Event PropertyChanged As EventHandler
    '    AddHandler(ByVal value As EventHandler)
    '        Dim handler2 As EventHandler
    '        Dim propertyChanged As EventHandler = Me.PropertyChanged
    '        Do
    '            handler2 = propertyChanged
    '            Dim handler3 As EventHandler = DirectCast(Delegate.Combine(handler2, value), EventHandler)
    '            propertyChanged = Interlocked.CompareExchange(Of EventHandler)((Me.PropertyChanged), handler3, handler2)
    '        Loop While (Not propertyChanged Is handler2)
    '    End AddHandler
    '    RemoveHandler(ByVal value As EventHandler)
    '        Dim handler2 As EventHandler
    '        Dim propertyChanged As EventHandler = Me.PropertyChanged
    '        Do
    '            handler2 = propertyChanged
    '            Dim handler3 As EventHandler = DirectCast(Delegate.Remove(handler2, value), EventHandler)
    '            propertyChanged = Interlocked.CompareExchange(Of EventHandler)((Me.PropertyChanged), handler3, handler2)
    '        Loop While (Not propertyChanged Is handler2)
    '    End RemoveHandler
    'End Event

    ' Methods
    Public Sub New(ByVal providerName As String)
        Me._providerName = providerName
        Me._connectionStringBuilder = DbProviderFactories.GetFactory(providerName).CreateConnectionStringBuilder
        Me._connectionStringBuilder.BrowsableConnectionString = False
    End Sub

    Public Overridable Sub Add(ByVal propertyName As String) Implements IDataConnectionProperties.Add
        If Not Me._connectionStringBuilder.ContainsKey(propertyName) Then
            Me._connectionStringBuilder.Add(propertyName, String.Empty)
            Me.OnPropertyChanged(EventArgs.Empty)
        End If
    End Sub

    Public Overridable Function Contains(ByVal propertyName As String) As Boolean Implements IDataConnectionProperties.Contains
        Return Me._connectionStringBuilder.ContainsKey(propertyName)
    End Function

    Protected Overridable Function GetProperties(ByVal attributes As Attribute()) As PropertyDescriptorCollection
        Return TypeDescriptor.GetProperties(Me._connectionStringBuilder, attributes)
    End Function

    Protected Overridable Sub Inspect(ByVal connection As DbConnection)
    End Sub

    Protected Overridable Sub OnPropertyChanged(ByVal e As EventArgs)
        'If (Not Me.PropertyChanged Is Nothing) Then
        '    Me.PropertyChanged.Invoke(Me, e)
        'End If
        RaiseEvent PropertyChanged(Me, e)
    End Sub

    Public Overridable Sub Parse(ByVal s As String) Implements IDataConnectionProperties.Parse
        Me._connectionStringBuilder.ConnectionString = s
        Me.OnPropertyChanged(EventArgs.Empty)
    End Sub

    Public Overridable Sub Remove(ByVal propertyName As String) Implements IDataConnectionProperties.Remove
        If Me._connectionStringBuilder.ContainsKey(propertyName) Then
            Me._connectionStringBuilder.Remove(propertyName)
            Me.OnPropertyChanged(EventArgs.Empty)
        End If
    End Sub

    Public Overridable Sub Reset() Implements IDataConnectionProperties.Reset
        Me._connectionStringBuilder.Clear()
        Me.OnPropertyChanged(EventArgs.Empty)
    End Sub

    Public Overridable Sub Reset(ByVal propertyName As String) Implements IDataConnectionProperties.Reset
        If Me._connectionStringBuilder.ContainsKey(propertyName) Then
            Me._connectionStringBuilder.Remove(propertyName)
            Me.OnPropertyChanged(EventArgs.Empty)
        End If
    End Sub

#Region "Private System.ComponentModel Functions"
    Private Function ICustomTypeDescriptor_GetAttributes() As AttributeCollection Implements ICustomTypeDescriptor.GetAttributes
        Return TypeDescriptor.GetAttributes(Me._connectionStringBuilder, True)
    End Function

    Private Function ICustomTypeDescriptor_GetClassName() As String Implements ICustomTypeDescriptor.GetClassName
        Return TypeDescriptor.GetClassName(Me._connectionStringBuilder, True)
    End Function

    Private Function ICustomTypeDescriptor_GetComponentName() As String Implements ICustomTypeDescriptor.GetComponentName
        Return TypeDescriptor.GetComponentName(Me._connectionStringBuilder, True)
    End Function

    Private Function ICustomTypeDescriptor_GetConverter() As TypeConverter Implements ICustomTypeDescriptor.GetConverter
        Return TypeDescriptor.GetConverter(Me._connectionStringBuilder, True)
    End Function

    Private Function ICustomTypeDescriptor_GetDefaultEvent() As EventDescriptor Implements ICustomTypeDescriptor.GetDefaultEvent
        Return TypeDescriptor.GetDefaultEvent(Me._connectionStringBuilder, True)
    End Function

    Private Function ICustomTypeDescriptor_GetDefaultProperty() As PropertyDescriptor Implements ICustomTypeDescriptor.GetDefaultProperty
        Return Me.DefaultProperty
    End Function

    Private Function ICustomTypeDescriptor_GetEditor(ByVal editorBaseType As Type) As Object Implements ICustomTypeDescriptor.GetEditor
        Return TypeDescriptor.GetEditor(Me._connectionStringBuilder, editorBaseType, True)
    End Function

    Private Function ICustomTypeDescriptor_GetEvents() As EventDescriptorCollection Implements ICustomTypeDescriptor.GetEvents
        Return TypeDescriptor.GetEvents(Me._connectionStringBuilder, True)
    End Function

    Private Function ICustomTypeDescriptor_GetEvents(ByVal attributes As Attribute()) As EventDescriptorCollection Implements ICustomTypeDescriptor.GetEvents
        Return TypeDescriptor.GetEvents(Me._connectionStringBuilder, attributes, True)
    End Function

    Private Function ICustomTypeDescriptor_GetProperties() As PropertyDescriptorCollection Implements ICustomTypeDescriptor.GetProperties
        Return Me.GetProperties(New Attribute(0 - 1) {})
    End Function

    Friend Function ICustomTypeDescriptor_GetProperties(ByVal attributes As Attribute()) As PropertyDescriptorCollection Implements ICustomTypeDescriptor.GetProperties
        Return Me.GetProperties(attributes)
    End Function

    Private Function GetPropertyOwner(ByVal pd As PropertyDescriptor) As Object Implements ICustomTypeDescriptor.GetPropertyOwner
        Return Me._connectionStringBuilder
    End Function
#End Region


    Public Overridable Sub Test() Implements IDataConnectionProperties.Test
        Dim str As String = Me.ToTestString
        If ((str Is Nothing) OrElse (str.Length = 0)) Then
            Throw New InvalidOperationException(My.Resources.AdoDotNetConnectionProperties_NoProperties)
        End If
        'Dim connection As DbConnection = Nothing
        Using connection = DbProviderFactories.GetFactory(Me._providerName).CreateConnection
            connection.ConnectionString = str
            connection.Open()
            Me.Inspect(connection)
        End Using
    End Sub

    Public Overridable Function ToDisplayString() As String Implements IDataConnectionProperties.ToDisplayString
        Dim connectionString As String
        Dim properties As PropertyDescriptorCollection = Me.GetProperties(New Attribute() {PasswordPropertyTextAttribute.Yes})
        Dim list As New List(Of KeyValuePair(Of String, Object))
        Dim descriptor As PropertyDescriptor
        For Each descriptor In properties
            Dim displayName As String = descriptor.DisplayName
            If Me.ConnectionStringBuilder.ShouldSerialize(displayName) Then
                list.Add(New KeyValuePair(Of String, Object)(displayName, Me.ConnectionStringBuilder.Item(displayName)))
                Me.ConnectionStringBuilder.Remove(displayName)
            End If
        Next
        Try
            connectionString = Me.ConnectionStringBuilder.ConnectionString
        Finally
            Dim pair As KeyValuePair(Of String, Object)
            For Each pair In list
                If (Not pair.Value Is Nothing) Then
                    Me.ConnectionStringBuilder.Item(pair.Key) = pair.Value
                End If
            Next
        End Try
        Return connectionString
    End Function

    Public Overridable Function ToFullString() As String Implements IDataConnectionProperties.ToFullString
        Return Me._connectionStringBuilder.ConnectionString
    End Function

    Public Overrides Function ToString() As String
        Return Me.ToFullString
    End Function

    Protected Overridable Function ToTestString() As String
        Return Me._connectionStringBuilder.ConnectionString
    End Function


    ' Properties
    Public ReadOnly Property ConnectionStringBuilder As DbConnectionStringBuilder
        Get
            Return Me._connectionStringBuilder
        End Get
    End Property

    Protected Overridable ReadOnly Property DefaultProperty As PropertyDescriptor
        Get
            Return TypeDescriptor.GetDefaultProperty(Me._connectionStringBuilder, True)
        End Get
    End Property

    Public Overridable ReadOnly Property IsComplete As Boolean Implements IDataConnectionProperties.IsComplete
        Get
            Return True
        End Get
    End Property

    Public Overridable ReadOnly Property IsExtensible As Boolean Implements IDataConnectionProperties.IsExtensible
        Get
            Return Not Me._connectionStringBuilder.IsFixedSize
        End Get
    End Property

    Default Public Overridable Property Item(ByVal propertyName As String) As Object Implements IDataConnectionProperties.Item
        Get
            If (propertyName Is Nothing) Then
                Throw New ArgumentNullException("propertyName")
            End If
            Dim obj2 As Object = Nothing
            If Not Me._connectionStringBuilder.TryGetValue(propertyName, obj2) Then
                Return Nothing
            End If
            If Me._connectionStringBuilder.ShouldSerialize(propertyName) Then
                Return Me._connectionStringBuilder.Item(propertyName)
            End If
            Dim obj3 As Object = Me._connectionStringBuilder.Item(propertyName)
            If (Not obj3 Is Nothing) Then
                Return obj3
            End If
            Return DBNull.Value
        End Get
        Set(ByVal value As Object)
            If (propertyName Is Nothing) Then
                Throw New ArgumentNullException("propertyName")
            End If
            Me._connectionStringBuilder.Remove(propertyName)
            If (value Is DBNull.Value) Then
                Me.OnPropertyChanged(EventArgs.Empty)
            Else
                Dim obj2 As Object = Nothing
                Me._connectionStringBuilder.TryGetValue(propertyName, obj2)
                Me._connectionStringBuilder.Item(propertyName) = value
                If Object.Equals(obj2, value) Then
                    Me._connectionStringBuilder.Remove(propertyName)
                End If
                Me.OnPropertyChanged(EventArgs.Empty)
            End If
        End Set
    End Property


    ' Fields
    Private _connectionStringBuilder As DbConnectionStringBuilder
    Private _providerName As String
    Private _PropertyChanged As EventHandler
End Class

